<template>
  <el-container id="app" class="h-100" v-loading="loading" :element-loading-text="loadingText">
    <el-aside width="220px" class="h-100 color-eee" v-if="appVisible">
      <div class="aside-header flex align-center justify-center">Library List</div>
      <div class="padding-x-20">
        <el-divider></el-divider>
      </div>
      <el-menu
        default-active="0"
        class="padding-y-10"
        background-color="#3C4852"
        text-color="#fff"
        active-text-color="#ffd04b"
        @select="onSelectRepo"
      >
        <el-menu-item v-for="(repo, i) in repoList" :key="repo.libName" :index="i.toString()">
          <img class="lib-logo margin-right-10" :src="repo.logo" />
          <span slot="title">{{ repo.libName }}</span>
        </el-menu-item>
      </el-menu>
    </el-aside>
    <el-container v-if="appVisible">
      <el-header class="h-auto">
        <div class="flex justify-between padding-y-10">
          <div>Weapp Components Manager</div>
          <div>
            <el-link
              type="primary"
              class="margin-left-20"
              @click="openWithDefaultBrowser('https://gitee.com/microkof/Weapp-Components-Manager')"
            >Offical Site</el-link>
          </div>
        </div>
      </el-header>
      <el-main>
        <el-tabs
          :value="activeProjIndex.toString()"
          type="card"
          editable
          @tab-click="activeProjIndex = parseInt($event.name)"
          @tab-remove="removeProj"
          @tab-add="addProj"
        >
          <el-tab-pane
            v-for="(proj, i) in projList"
            :key="i"
            :label="proj.projName"
            :name="i.toString()"
          ></el-tab-pane>
        </el-tabs>
        <div class="margin-bottom-10">
          <el-button @click="editProj">Edit Project</el-button>
          <el-button @click="rebuildProj" title="当组件目录不慎损毁时使用">Rebuild Project</el-button>
          <el-button type="success" @click="openWithDefaultBrowser(activeRepo.officalDocs)">
            Library's Offical Docs
          </el-button>
          <el-tooltip v-show="activeRepo.attentions" class="item" effect="dark" :content="activeRepo.attentions" placement="top">
            <el-button type="danger">Attentions</el-button>
          </el-tooltip>
        </div>
        <component-checkbox-list
          :repoList="repoList"
          :repoIndex="activeRepoIndex"
          :projList="projList"
          :projIndex="activeProjIndex"
          @changeCheckbox="changeCheckbox"
        />
      </el-main>
    </el-container>

    <el-dialog
      :title="formInCreation ? '创建项目' : '修改项目'"
      :visible.sync="dialogVisible"
      :show-close="!!projList.length"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      width="640px"
    >
      <el-form ref="dialogForm" :model="form" :rules="rules">
        <el-form-item label="项目名称" label-width="80px" prop="projName">
          <el-input v-model="form.projName" autocomplete="off" placeholder="填写小程序项目的项目名称" />
        </el-form-item>
        <el-form-item label="组件位置" label-width="80px" prop="projDirectory">
          <el-input
            v-model="form.projDirectory"
            autocomplete="off"
            placeholder="小程序项目中存放组件的路径，建议以 wxcomponents 结尾"
            readonly
          >
            <el-button slot="append" @click="openDirDialog">选择目录</el-button>
          </el-input>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button v-show="!formInCreation" type="default" @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="submitForm">{{ formInCreation ? '创 建' : '修 改' }}</el-button>
      </span>
    </el-dialog>
  </el-container>
</template>

<script>
const { shell } = require('electron');
import path from 'path';
import fsExtra from 'fs-extra';
import repoList from '@/json/repo';
import {
  repoPackageExist,
  getRepoNewVersion,
  downloadRepo,
  uncompressRepo,
  emptyRepoPackage,
  getRepoComponentList,
} from '@/utils/repo';
import { readProjList, writeProjList } from '@/utils/proj';
import { copyComponent, removeComponent, getDependencies } from '@/utils/component';
import componentCheckboxList from '@/components/componentCheckboxList/componentCheckboxList.vue';
import { getDifferenceSet } from '@/utils/array';

export default {
  name: 'App',
  components: {
    componentCheckboxList,
  },
  data() {
    return {
      appVisible: false,
      repoList,
      activeRepoIndex: 0,
      projList: [],
      activeProjIndex: 0,
      dialogVisible: false,
      form: {
        projName: '',
        projDirectory: '',
      },
      formInCreation: true,
      rules: {
        projName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
        projDirectory: [{ required: true, message: '请选择组件根目录', trigger: 'blur' }],
      },
      loading: false,
      loadingText: '',
    };
  },
  computed: {
    activeProj() {
      return this.projList[this.activeProjIndex];
    },
    activeRepo() {
      return this.repoList[this.activeRepoIndex];
    },
    activeLibOfProj() {
      return this.activeProj.libList[this.activeRepoIndex];
    },
    repoPackageRoot() {
      return path.join(
        this.$globalConst.reposPath,
        this.activeRepo.repoName,
        'package',
        this.activeRepo.distFolder,
      );
    },
  },
  created() {
    this.init();
  },
  methods: {
    openWithDefaultBrowser(url) {
      event.preventDefault();
      shell.openExternal(url);
    },
    init() {
      const projListJSON = readProjList.bind(this)();

      if (!projListJSON) {
        this.dialogVisible = true;
        return;
      }

      this.loadingText = '正在检查组件库是否有更新，这需要一点点时间';
      this.loading = true;
      /** 对 repoList 唯一的写操作：计算每个repo的componentList并写入 */
      return Promise.all(
        this.repoList.map((repo) => new Promise((resolve) => {
          if (!repoPackageExist.bind(this)(repo)) {
            return getRepoNewVersion
              .bind(this)(repo)
              .then((value) => {
                repo.version = value;
                repo.checkNew = true;
                return downloadRepo.bind(this)(repo);
              })
              .then(() => uncompressRepo.bind(this)(repo))
              .then(() => {
                repo.componentList = getRepoComponentList.bind(this)(repo);
                resolve();
              });
          }
          repo.componentList = getRepoComponentList.bind(this)(repo);
          resolve();
        }))
      ).then(() => {
        /** 计算 projList 每个 proj 的每个 lib 的 dependencies */
        projListJSON.forEach((proj) => {
          proj.libList.forEach((lib, i) => {
            if (lib.selectionList.length) {
              const repoPackageRoot = path.join(
                this.$globalConst.reposPath,
                this.repoList[i].repoName,
                'package',
                this.repoList[i].distFolder,
              );
              lib.dependencyList = getDependencies.bind(this)(lib, repoPackageRoot);
            }
          });
        });
        // 最后赋值
        this.projList = projListJSON;
      })
        .then(() => {
          /** 检查库更新 */
          console.log('检查更新开始');
          return Promise.all(
            this.repoList.map((repo, repoIndex) => {
              if (repo.checkNew) {
                return Promise.resolve();
              }

              return getRepoNewVersion
                .bind(this)(repo)
                .then((version) => {
                  if (repo.version === version) {
                    return Promise.resolve();
                  }

                  repo.version = version;
                  repo.checkNew = true;
                  emptyRepoPackage.bind(this)(repo);
                  return downloadRepo
                    .bind(this)(repo)
                    .then(() => uncompressRepo.bind(this)(repo))
                    .then(() => {
                      repo.componentList = getRepoComponentList.bind(this)(repo);
                      /** 计算所有proj的每个lib的依赖，清空所有proj的该lib的组件，重新copy组件 */
                      this.projList.forEach((proj) => {
                        const repoPackageRoot = path.join(
                          this.$globalConst.reposPath,
                          repo.repoName,
                          'package',
                          repo.distFolder,
                        );
                        if (proj.libList[repoIndex] && proj.libList[repoIndex].selectionList.length) {
                          proj.libList[repoIndex].dependencyList = getDependencies.bind(this)(
                            proj.libList[repoIndex],
                            repoPackageRoot,
                          );
                          fsExtra.emptyDirSync(path.join(proj.projDirectory, repo.repoName));
                          proj.libList[repoIndex].dependencyList.forEach((component) => {
                            const from = path.join(repoPackageRoot, component);
                            const to = path.join(proj.projDirectory, repo.repoName, component);
                            copyComponent(from, to);
                          });
                        }
                      });
                    });
                });
            }),
          );
        })
        .then(() => {
          console.log('检查更新结束');
          this.loading = false;
          this.appVisible = true;
        });
    },
    addProj() {
      this.formInCreation = true;
      this.form.projName = '';
      this.form.projDirectory = '';
      this.dialogVisible = true;
    },
    editProj() {
      this.formInCreation = false;
      this.form.projName = this.activeProj.projName;
      this.form.projDirectory = this.activeProj.projDirectory;
      this.dialogVisible = true;
    },
    rebuildProj() {
      this.activeProj.libList.forEach((lib, libIndex) => {
        const thisRepo = this.repoList[libIndex];
        const thisRepoPackageRoot = path.join(
          this.$globalConst.reposPath,
          thisRepo.repoName,
          'package',
          thisRepo.distFolder,
        );
        lib.dependencyList.forEach((component) => {
          const from = path.join(thisRepoPackageRoot, component);
          const to = path.join(this.activeProj.projDirectory, thisRepo.repoName, component);
          copyComponent(from, to);
        });
        this.$message(`${this.activeProj.projName} 组件库构建完成`);
      });
    },
    removeProj(index) {
      this.$confirm(
        `${'是否删除这个项目？<br>'
          + '项目名称：'}${
          this.projList[parseInt(index)].projName
        }<br>`
          + `组件位置：${
            this.projList[parseInt(index)].projDirectory
          }<br>`
          + '此操作只会删除项目记录，并不会删除项目本地文件。如需删除本地文件请另行删除。',
        '删除项目',
        {
          confirmButtonText: '删除',
          cancelButtonText: '取消',
          type: 'warning',
          dangerouslyUseHTMLString: true,
        },
      ).then(
        () => {
          this.projList.splice(parseInt(index), 1);
          writeProjList.bind(this)(this.projList);
          this.$message({
            type: 'success',
            message: '删除成功!',
          });
        },
        () => {},
      );
    },
    onSelectRepo(value) {
      this.activeRepoIndex = parseInt(value);
      if (!this.activeProj.libList[this.activeRepoIndex]) {
        for (let i = this.repoList.length - this.activeProj.libList.length; i > 0; i--) {
          this.activeProj.libList.push({
            selectionList: [],
            dependencyList: [],
          });
        }
        writeProjList.bind(this)(this.projList);
      }
    },
    changeCheckbox({ component, value }) {
      const oldDependencyList = [...this.activeLibOfProj.dependencyList];
      if (value) {
        this.activeLibOfProj.selectionList.push(component);
      } else {
        this.activeLibOfProj.selectionList.splice(
          this.activeLibOfProj.selectionList.indexOf(component),
          1,
        );
      }
      this.activeLibOfProj.dependencyList = getDependencies.bind(this)(
        this.activeLibOfProj,
        this.repoPackageRoot,
      );
      const differenceSet = getDifferenceSet(
        this.activeLibOfProj.dependencyList,
        value ? '>' : '<',
        oldDependencyList,
      );
      if (value) {
        differenceSet.forEach((c) => {
          const from = path.join(this.repoPackageRoot, c);
          const to = path.join(this.activeProj.projDirectory, this.activeRepo.repoName, c);
          copyComponent(from, to);
        });
      } else {
        differenceSet.forEach((c) => {
          const to = path.join(this.activeProj.projDirectory, this.activeRepo.repoName, c);
          removeComponent(to);
        });
      }
      writeProjList.bind(this)(this.projList);
    },
    openDirDialog() {
      const { dialog } = require('electron').remote;
      const projDirectory = dialog.showOpenDialogSync({
        properties: ['openDirectory'],
      });
      if (projDirectory) {
        this.form.projDirectory = projDirectory[0];
      }
    },
    submitForm() {
      this.$refs.dialogForm.validate((valid) => {
        if (valid) {
          this.dialogVisible = false;
          this.loadingText = '正在创建新项目';
          this.loading = true;
          if (this.formInCreation) {
            this.projList.push({
              projName: this.form.projName,
              projDirectory: this.form.projDirectory,
              libList: new Array(this.repoList.length).fill({
                selectionList: [],
                dependencyList: [],
              }),
            });
          } else {
            this.activeProj.projName = this.form.projName;
            if (this.activeProj.projDirectory !== this.form.projDirectory) {
              this.activeProj.projDirectory = this.form.projDirectory;
              this.rebuildProj();
            }
          }
          writeProjList.bind(this)(this.projList);
          this.init().then(() => {
            this.loading = false;
          });
        }
      });
    },
  },
};
</script>

<style lang="scss">
html,
body {
  height: 100%;
}

.el-aside {
  background-color: #3c4852;
  .aside-header {
    height: 134px;
  }
}

.el-main {
  padding-top: 0;
}

.el-divider {
  margin: 0;
}

.el-menu {
  border-right: none;
  .el-menu-item.is-active::after {
    content: '';
    position: absolute;
    top: 16px;
    right: 0;
    width: 0;
    height: 0;
    border: 12px solid transparent;
    border-right-color: #fff;
  }
}

.el-tabs__new-tab {
  color: #409eff;
}

.lib-logo {
  width: 24px;
  height: 24px;
}
</style>
